package com.tambapps.p2p.fandem

import com.tambapps.p2p.fandem.handshake.FandemReceiverHandshake
import com.tambapps.p2p.fandem.handshake.SenderHandshakeData
import com.tambapps.p2p.fandem.util.FileProvider
import com.tambapps.p2p.fandem.util.OutputStreamProvider
import com.tambapps.p2p.fandem.util.RecordingFileProvider
import com.tambapps.p2p.fandem.util.TransferListener
import com.tambapps.p2p.speer.Peer
import com.tambapps.p2p.speer.PeerConnection
import com.tambapps.p2p.speer.handshake.Handshake
import java.io.File
import java.io.IOException
import java.util.Optional
import java.util.concurrent.atomic.AtomicReference

class FileReceiver @JvmOverloads constructor(
    listener: TransferListener? = null,
    bufferSize: Int = DEFAULT_BUFFER_SIZE
) : FileSharer(listener) {
    private val connectionReference = AtomicReference<PeerConnection?>()
    private val handshake: Handshake
    private val bufferSize: Int

    init {
        handshake = FandemReceiverHandshake()
        this.bufferSize = bufferSize
    }

    @Throws(IOException::class)
    fun receiveFrom(peer: Peer?, fileProvider: FileProvider): List<File> {
        val recordingFileProvider = RecordingFileProvider(fileProvider)
        receiveFrom(peer, recordingFileProvider.toOutputStreamProvider())
        return recordingFileProvider.files
    }

    // just for Android 11+ :(, because Android deprecated the use of Java File class
    @Throws(IOException::class)
    fun receiveFrom(peer: Peer?, outputStreamProvider: OutputStreamProvider?) {
        PeerConnection.from(peer, handshake).use { connection ->
            connectionReference.set(connection)
            val data = connection.getHandshakeData<SenderHandshakeData>()
            listener?.onConnected(connection.selfPeer, connection.remotePeer)
            for (fileData in data.files) {
                val fileName = fileData.fileName!!
                val fileSize = fileData.fileSize
                val optExpectedChecksum: Optional<String> = fileData.getChecksum()
                outputStreamProvider!!.newOutputStream(fileName).use { fos ->
                    listener?.onTransferStarted(fileName, fileSize)
                    share(
                        connection.inputStream,
                        fos!!,
                        bufferSize,
                        fileName,
                        fileSize,
                        optExpectedChecksum.orElse(null)
                    )
                }
            }
        }
    }

    fun cancel() {
        val connection = connectionReference.get()
        if (connection != null) {
            try {
                connection.close()
            } catch (e: IOException) {
                // ignore it, it's just a cancel
            }
            connectionReference.set(null)
        }
    }
}
